package com.iwechat.api.mina;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;

import com.wctm.adapter.mina.entity.Active;
import com.wctm.adapter.mina.entity.ActiveResp;
import com.wctm.adapter.mina.entity.ConnectResp;
import com.wctm.adapter.mina.entity.Exit;
import com.wctm.adapter.mina.entity.ExitResp;
import com.wctm.adapter.mina.entity.PDU;
import com.wctm.adapter.mina.entity.SubmitResp;

/**
 * 客户端业务处理
 *
 * 
 */
public class NioClientHandlerAdapter extends IoHandlerAdapter {
    public static final String  RESPOOL_BEAN_NAME = "respPool";

    private static final Logger logger            = LogManager.getLogger();
    private static final String IS_LOGIN          = "_isLogin?_";
    private static final String CURR_WINDOW       = "_currWindow?_";
    private int                 maxRespPoolSize;
    private int                 sendWindow;
    private List<SubmitResp>    respPool;

    @Resource(name = NioClient.SESSION_POOL_BEAN_NAME)
    private List<IoSession>     sessionPool;

    @PostConstruct
    public void init() {
        respPool = Collections.synchronizedList(new LinkedList<SubmitResp>());
        logger.info("### " + this + " init succeed!");
    }

    @Bean(name = RESPOOL_BEAN_NAME)
    @Scope("singleton")
    public List<SubmitResp> getRespPool() {
        return respPool;
    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        logger.error("sessionId[" + session.getId() + "] exceptionCaught:", cause);
        sessionPool.remove(session);
        session.closeNow();
    }

    @Override
    public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
        logger.info("sessionId[" + session.getId() + "] sessionIdle.");
        Active active = new Active();
        session.write(active);
    }

    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        logger.info("sessionId[" + session.getId() + "] messageReceived:{}", message);

        if (message instanceof ConnectResp) {
            doConnectResp(session, (ConnectResp) message);
        } else if (message instanceof SubmitResp) {
            doSubmitResp(session, (SubmitResp) message);
        } else if (message instanceof Active) {
            doActive(session, (Active) message);
        } else if (message instanceof Exit) {
            doExit(session, (Exit) message);
        } else if (message instanceof ExitResp) {
            session.closeNow();
        }
    }

    /**
     * 接收消息发送响应
     *
     * 
     * @param session
     * @param message
     */
    private void doSubmitResp(IoSession session, SubmitResp message) {
        synchronized (session) {
            int curr = (Integer) session.getAttribute(CURR_WINDOW);
            curr++;
            if (curr > sendWindow) {
                curr = sendWindow;
            }
            session.getAttribute(CURR_WINDOW, curr);
        }
        // 消息不直接处理,异步缓存提供方法供客户端调用获取
        if (respPool.size() < maxRespPoolSize) {
            respPool.add(message);
        } else {
            logger.warn("sessionId[" + session.getId() + "] 请及时处理响应,已丢弃消息:{}", message);
        }
    }

    /**
     * 处理登录响应
     *
     * 
     * @param session
     * @param message
     */
    private void doConnectResp(IoSession session, ConnectResp message) {
        int code = message.getErrorCode();
        synchronized (session) {
            if (PDU.ERRORCODE_SUCCESS == code) {
                // 标记会话登录
                session.setAttribute(IS_LOGIN, true);
                // 建立连接后连接发送窗口值为窗口最大值
                session.setAttribute(CURR_WINDOW, sendWindow);

                session.notify();
            } else {
                session.notify();
                logger.error("sessionId[" + session.getId() + "] 登录失败,ErrorCode=" + code);
                session.closeNow();
            }
        }
    }

    /**
     * 处理登出请求
     *
     * 
     * @param session
     * @param message
     */
    private void doExit(IoSession session, Exit message) {
        ExitResp resp = new ExitResp(message);
        session.write(resp);

    }

    /**
     * 处理心跳请求
     *
     * 
     * @param session
     * @param message
     */
    private void doActive(IoSession session, Active message) {
        ActiveResp resp = new ActiveResp(message);
        session.write(resp);

    }

    @Override
    public void messageSent(IoSession session, Object message) throws Exception {
        logger.info("sessionId[" + session.getId() + "] messageSent:{}", message);
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        logger.info("sessionId[" + session.getId() + "] sessionClosed.");
        sessionPool.remove(session);
    }

    @Override
    public void sessionCreated(IoSession session) throws Exception {
        logger.info("sessionId[" + session.getId() + "] sessionCreated.");
    }

    @Override
    public void sessionOpened(IoSession session) throws Exception {
        logger.info("sessionId[" + session.getId() + "] sessionOpened.");
    }

    public int getMaxRespPoolSize() {
        return maxRespPoolSize;
    }

    public void setMaxRespPoolSize(int maxRespPoolSize) {
        this.maxRespPoolSize = maxRespPoolSize;
    }

    public void setSendWindow(int sendWindow) {
        this.sendWindow = sendWindow;
    }
}
